

#include "SienaInterface.h"


SienaInterface::SienaInterface(): outputInterface()
{
	fwdTable = new siena::FwdTable();

	onBuilding = false;
	toBuild = false;
	buildingTableCond = new pthread_cond_t;
	pthread_cond_init(buildingTableCond, NULL);
	buildingTableMutex = new pthread_mutex_t;
	pthread_mutex_init(buildingTableMutex, NULL);
	buildingThread = new pthread_t;
	pthread_create(buildingThread, NULL, siena_interface::startBuildForwardingTable, (void *) this);

}

SienaInterface::~SienaInterface(){
//	delete fwdTable;
}

void SienaInterface::installSubscription(Inbox* inbox){

	pthread_mutex_lock(buildingTableMutex);
	if (onBuilding) {
		cout << "Subscription manager: SienaFF: waiting consolidation table..." << endl;
		pthread_cond_wait(buildingTableCond, buildingTableMutex);
	}
	cout << "Subscription manager: SienaFF: start installing subscription..." << endl;
	if_t ifId = inbox->getClient();

//	boost::unordered_map<if_t, std::set<simple_filter *> >::iterator it = sienaForwardingMap.find(ifId);
	boost::unordered_map<if_t, simple_predicate* >::iterator it = newsienaForwardingMap.find(ifId);
	if (it != newsienaForwardingMap.end()){
		simple_filter *filter = translate(inbox->getSubPkt());
		//it->second.insert(filter);
		it->second->add(filter);
	} else {
		simple_filter *filter = translate(inbox->getSubPkt());
		//std::set<simple_filter*> filterSet;
		//filterSet.insert(filter);
		//sienaForwardingMap.insert(make_pair(ifId, filterSet));
		simple_predicate *predicate = new simple_predicate();
		predicate->add(filter);
		newsienaForwardingMap.insert(make_pair(ifId, predicate));

	}
	toBuild = true;
	cout << "Subscription manager: SienaFF: subscription installed." << endl;
	pthread_cond_signal(buildingTableCond);
	pthread_mutex_unlock(buildingTableMutex);

}

void SienaInterface::deleteSubscription(Inbox* inbox){
/*
	pthread_mutex_lock(buildingTableMutex);
	if (onBuilding) {
		pthread_cond_wait(buildingTableCond, buildingTableMutex);
	}
	if_t ifId = inbox->getClient();
	//boost::unordered_map<if_t, std::set<simple_filter *> >::iterator it = sienaForwardingMap.find(ifId);
	boost::unordered_map<if_t, simple_predicate *>::iterator it = newsienaForwardingMap.find(ifId);
	if (it != newsienaForwardingMap.end()){
		boost::unordered_map<BackSubPkt*, simple_filter*>::iterator itSub = subscriptionToFilterMap.find(inbox->getUnSubPkt()->getSubPkt());
		if (itSub != subscriptionToFilterMap.end()){
			simple_filter* sf = itSub->second;
			std::set<simple_filter *>::iterator it2 = it->second.find(sf);
			if (it2 != it->second.end()){
				it->second.erase(it2);
				subscriptionToFilterMap.erase(itSub);
				if (it->second.empty()){
					sienaForwardingMap.erase(it);
				}
				toBuild = true;
			}
		}
	}
	pthread_cond_signal(buildingTableCond);
	pthread_mutex_unlock(buildingTableMutex);
*/
}


void SienaInterface::processEvent(PubPkt* pkt, Outbox &outbox){
	pthread_mutex_lock(buildingTableMutex);
	if (onBuilding) {
		cout << "Subscription manager: SienaFF: waiting consolidation table..." << endl;
		pthread_cond_wait(buildingTableCond, buildingTableMutex);
	}
	cout << "Subscription manager: SienaFF: start processing..." << endl;
	outputInterface.clear();
	simple_message *message = translate(pkt);
	fwdTable->match(*message, outputInterface);
	outbox.pkts.insert(pkt);
	outbox.clients = outputInterface.getMatchedClients();
	//std::set<if_t> matched = outputInterface.getMatchedClients();
	//std::set<PubPkt*> pktSet;
	//pktSet.insert(pkt);
/*	outbox.pkts.insert(pkt);
	std::set<int> clientsMatched;
	for (std::set<if_t>::iterator it = matched.begin(); it!=matched.end(); it++){
		int clientId = *it;
		clientsMatched.insert(clientId);
	}
	outbox.clients = clientsMatched;
*/
	//Outbox* outbox = new Outbox(clientsMatched, pktSet);
	//outputInterface->clear();
	pthread_cond_signal(buildingTableCond);
	pthread_mutex_unlock(buildingTableMutex);
	cout << "Subscription manager: SienaFF: forwarding executed." << endl;
}

void SienaInterface::buildForwardingTable(){
	while(true){
		pthread_mutex_lock(buildingTableMutex);
		while (!toBuild) {
			cout << "Subscription manager: SienaFF: waiting for forwarding table to update." << endl;
			pthread_cond_wait(buildingTableCond, buildingTableMutex);
		}
		onBuilding = true;
		cout << "Subscription manager: SienaFF: start updating forwarding table..." << endl;
		fwdTable->clear(); // O recycle_clear??
	/*	for (boost::unordered_map<if_t, std::set<simple_filter *> >::iterator it2 = sienaForwardingMap.begin(); it2!=sienaForwardingMap.end(); it2++) {
			if_t ifId = it2->first;
			simple_predicate predicate;
			for (set<simple_filter *>::iterator it3=it2->second.begin(); it3!=it2->second.end(); it3++) {
				simple_filter *sf = *it3;
				predicate.add(sf);
			}
			fwdTable->ifconfig(ifId, predicate);
		}
	*/
		for (boost::unordered_map<if_t, simple_predicate* >::iterator it2 = newsienaForwardingMap.begin(); it2!=newsienaForwardingMap.end(); it2++) {
			simple_predicate* predTemp = it2->second;
			fwdTable->ifconfig(it2->first, *predTemp);
		}
		fwdTable->consolidate();
		onBuilding = false;
		toBuild = false;
		cout << "Subscription manager: SienaFF: forwarding table updated." << endl;
		pthread_cond_signal(buildingTableCond);
		pthread_mutex_unlock(buildingTableMutex);
	}
}


void * siena_interface::startBuildForwardingTable(void *input){
	SienaInterface *interface = (SienaInterface*) input;
	interface->buildForwardingTable();
	pthread_exit(NULL);
	return 0;
}


bool SienaInterface::SienaOutputInterface::output(if_t ifId) throw(){
	int client = ifId;
	matchedClients.insert(client);
	return false;
}

void SienaInterface::SienaOutputInterface::clear(){
	matchedClients.clear();
}


simple_filter* SienaInterface::translate(BackSubPkt* pkt){
	simple_filter *filter = new simple_filter();

	siena::operator_id opId = siena::eq_id;
	siena::int_t intVal = siena::int_t(pkt->getEventType());
	simple_op_value *opValue = new simple_op_value(opId, intVal);
	char *arrayNameEvent = new char[5];
	arrayNameEvent[0] = 't';
	arrayNameEvent[1] = 'y';
	arrayNameEvent[2] = 'p';
	arrayNameEvent[3] = 'e';
	arrayNameEvent[4] = '\0';
	siena::string_t name(arrayNameEvent);
	filter->add(name, opValue);

	for (int i=0; i<pkt->getConstraintsNum(); i++){
		siena::operator_id opId;
		if (pkt->getConstraint(i).op == EQ){
			opId = siena::eq_id;
		}
		if (pkt->getConstraint(i).op == GT){
			opId = siena::gt_id;
		}
		if (pkt->getConstraint(i).op == LT){
			opId = siena::lt_id;
		}

		simple_op_value *opValue;
		if (pkt->getConstraint(i).type == INT){

			siena::int_t intVal = siena::int_t(pkt->getConstraint(i).intVal);
			opValue = new simple_op_value(opId, intVal);
		}
		if (pkt->getConstraint(i).type == FLOAT){

		}
		if (pkt->getConstraint(i).type == BOOL){

			siena::bool_t boolVal = pkt->getConstraint(i).boolVal;
			opValue = new simple_op_value(opId, boolVal);
		}
		if (pkt->getConstraint(i).type == STRING){
			char *arrayString = new char[STRING_VAL_LEN];
			int k=0;
			while (pkt->getConstraint(i).stringVal[k] != '\0'){
				arrayString[k] = pkt->getConstraint(i).stringVal[k];
				k++;
			}
			arrayString[k] = '\0';
			siena::string_t stringVal(arrayString);
			opValue = new simple_op_value(opId, stringVal);
		}

		char *arrayName = new char[NAME_LEN];
		int j=0;
		while (pkt->getConstraint(i).name[j] != '\0'){
			arrayName[j] = pkt->getConstraint(i).name[j];
			j++;
		}
		arrayName[j] = '\0';
		siena::string_t name(arrayName);

		filter->add(name, opValue);
	}
	cout << "Subscription manager: SienaFF: subscription translated." << endl;
	return filter;
}


simple_message* SienaInterface::translate(PubPkt* pkt){
	simple_message *message = new simple_message();

	char *arrayNameType = new char[5];
	arrayNameType[0] = 't';
	arrayNameType[1] = 'y';
	arrayNameType[2] = 'p';
	arrayNameType[3] = 'e';
	arrayNameType[4] = '\0';
	siena::string_t name(arrayNameType);

	siena::int_t intVal = siena::int_t(pkt->getEventType());
	simple_value *simpleValue = new simple_value(intVal);
	message->add(name, simpleValue);

	for (int i=0; i<pkt->getAttributesNum(); i++){
		simple_value *simpleValue;
		if (pkt->getAttribute(i).type == INT){
			siena::int_t intVal = siena::int_t(pkt->getAttribute(i).intVal);
			simpleValue = new simple_value(intVal);
		}
		if (pkt->getAttribute(i).type == FLOAT){

		}
		if (pkt->getAttribute(i).type == BOOL){
			siena::bool_t boolVal = pkt->getAttribute(i).boolVal;
			simpleValue = new simple_value(boolVal);
		}
		if (pkt->getAttribute(i).type == STRING){
			char *arrayString = new char[STRING_VAL_LEN];
			int k=0;
			while (pkt->getAttribute(i).stringVal[k] != '\0'){
				arrayString[k] = pkt->getAttribute(i).stringVal[k];
				k++;
			}
			arrayString[k] = '\0';
			siena::string_t stringVal(arrayString);
			simpleValue = new simple_value(stringVal);
		}

		char *arrayName = new char[NAME_LEN];
		int j=0;
		while (pkt->getAttribute(i).name[j] != '\0'){
			arrayName[j] = pkt->getAttribute(i).name[j];
			j++;
		}
		arrayName[j] = '\0';
		siena::string_t name(arrayName);

		message->add(name, simpleValue);
	}
	cout << "Subscription manager: SienaFF: message translated." << endl;
	return message;
}




void SienaInterface::test(){

		if_t ifId = 6;

		simple_filter *filter = new simple_filter();

		char *arrayName = new char[7];
		arrayName[0] = 'v';
		arrayName[1] = 'a';
		arrayName[2] = 'l';
		arrayName[3] = 'o';
		arrayName[4] = 'r';
		arrayName[5] = 'e';
		arrayName[6] = '\0';
		siena::string_t name(arrayName);

		siena::int_t intVal = siena::int_t(8);
		siena::operator_id opId = siena::gt_id;
		simple_op_value *opValue = new simple_op_value(opId, intVal);

		filter->add(name, opValue);

		siena::FwdTable *table = new siena::FwdTable();

		simple_predicate sp;
		sp.add(filter);
		table->ifconfig(ifId, sp);
		table->consolidate();


		simple_message *message = new simple_message();

		char *arrayNameEvent = new char[7];
		arrayNameEvent[0] = 'v';
		arrayNameEvent[1] = 'a';
		arrayNameEvent[2] = 'l';
		arrayNameEvent[3] = 'o';
		arrayNameEvent[4] = 'r';
		arrayNameEvent[5] = 'e';
		arrayNameEvent[6] = '\0';
		siena::string_t nameEvento(arrayNameEvent);

		siena::int_t intValEvento = siena::int_t(11);
		simple_value *simpleValueEvento = new simple_value(intValEvento);

		message->add(nameEvento, simpleValueEvento);

		simple_handler mh;

		table->match(*message, mh);

		int numResults = mh.getNumOutput();

		cout << "metched: " << numResults << endl;

		mh.clear();

		return;
}




